package siedler.GUI.gameElements;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.ArrayList;

import siedler.GUI.SiedlerGamePanel;
import siedler.GUI.interfaces.Klickable;
import siedler.GUI.interfaces.Paintable;
import siedler.GUI.other.Coordinate;
import siedler.gaming.Resources;
import util.simpleIO.Out;

public class Field implements Paintable, Klickable, Serializable {

	//-----------------------------------------------------------------
	// --- Graphic variables ------------------------------------------
	//
	
	public final static double fieldSpaceFactor    = 0.88;		// percentage of space to actually use
	private final static int   x6E  = SiedlerGamePanel.x6E;		// number of edges/connection
	private final static int   xAdd = SiedlerGamePanel.xAdd;	// value to add before modulo for index <= 0
	//public final static SiedlerGamePanel gamePanel = null;
	
	private final static Polygon  p = new Polygon();
	protected static Coordinate[] outerCornersRelCoord;
	protected static Coordinate[] innerCornersRelCoord;
	
	private static int size = 0;
	private final int fieldID;
	private Connection[] connections;
	private Corner[]     corners;
	private Coordinate position   = zeroCoord;
	private Coordinate klickCoord = zeroCoord;
	private Coordinate positionT1 = zeroCoord, positionT2 = zeroCoord;
	
	//-----------------------------------------------------------------
	// --- Game specific methods ------------------------------------
	//
	public int diceNumber;
	public int resourceNumber;
	
	boolean mhasBandit = false;
	
	public void reset(){
		mhasBandit = false;
	}
	
	
	//-----------------------------------------------------------------
	// --- Initialization ---------------------------------------------
	//
	
	/**
	 * Full Constructor
	 * @param
	 */
	public Field(int fieldNumber) {
		//initGraphics(position,size);
		this.fieldID = fieldNumber;
		
		//if(gamePanel==null)
		//	throw new RuntimeException("Initiate static variables before Constructur!");
	}
	
	/**
	 * initGraphics
	 * Initialize the coordinates and the size of the field.
	 *  -> Position
	 *  -> Klick Area
	 *  -> Size
	 *  
	 * @param coordinate	- position
	 * @param size			- height and width
	 * @return true if size was different to last call (-> recalculated)
	 */
	public boolean initGraphics(Coordinate position, int size) {
		
		this.position 	= position;
		this.klickCoord	= new Coordinate(position.x1, position.y1, (int)(size*0.26));
		int textMoveFactor = 8;
		this.positionT1 = position.copy().add(0, -size/textMoveFactor);
		this.positionT2 = position.copy().add(0,  size/textMoveFactor);
		
		if(Field.size==size)
			return false;
		Field.size = size;
		calculateHexagon_rel(size/2);
		
		Out.pl();
		Out.pl("Size/"+textMoveFactor+" = " + size/textMoveFactor);
		Out.pl("pos      " + position);
		Out.pl("posT1    " + positionT1);
		Out.pl("posT2    " + positionT2);
		
		return true;
	} 
	

	
	//-----------------------------------------------------------------
	// --- object specific methods ------------------------------------
	//
	
	/**
	 * getFieldNumber
	 * @return the number of this field (1...19)
	 */
	public int getFieldNumber(){
		return fieldID;
		//return gamePanel.getFieldNumber(this);
	}
	
	/**
	 * getFieldSize
	 * @return size of this field
	 */
	public static int getFieldSize(){
		return size;
	}
	
	
	/**
	 * Set connections to other fields
	 * @param connections - array with a length of 6
	 */
	public void setConnections(Connection[] connections){
		if(connections.length != x6E)
			throw new RuntimeException("Length must be 'numberOfConnections'.");
		this.connections = connections;
	}
	/**
	 * getConnections
	 * @return all 6 connections
	 */
	public Connection[] getConnections(){
		return connections;
	}
	/**
	 * getConnection per number
	 * supports negative numbers
	 * @param connectionNumber
	 * @return connection
	 */
	public Connection getConnection(int connectionNumber){
		int index = ( xAdd + connectionNumber-1 ) % x6E;
		return connections[index];
	}
	/**
	 * getConnectionNumber
	 * @param c1 - connection
	 * @return number of connection 'c1' (1...6, starting on top-right)
	 */
	public int getConnectionNumber(Connection c1){
		for(int i=0; i<connections.length; i++){
			if(connections[i]==c1){
				return i+1;
			}
		}
		throw new RuntimeException("Connection 'c1' not found.");
	}
	/**
	 * nextConnection (clockwise direction)
	 * @param c1
	 * @return the next connection around this field
	 */
	public Connection nextConnection(Connection c1){
		for(int i=0; i<connections.length; i++){
			if(connections[i]==c1){
				int nIndex = (i+1)%x6E;
				return connections[nIndex];
			}
		}
		throw new RuntimeException("Connection 'c1' not found.");
	}
	/**
	 * previousConnection (clockwise direction)
	 * @param c1
	 * @return the privious connection around this field.
	 */
	public Connection previousConnection(Connection c1){
		for(int i=0; i<connections.length; i++){
			if(connections[i]==c1){
				int nIndex = (i-1+x6E)%x6E;
				return connections[nIndex];
			}
		}
		throw new RuntimeException("Connection 'c1' not found.");
	}
	/**
	 * getFirstCorner
	 * @param connection
	 * @return first corner of this connection
	 */
	public Corner getFirstCorner(Connection c1){
		for(int i=0; i<connections.length; i++){
			if(connections[i]==c1){
				return corners[i];
			}
		}
		throw new RuntimeException("Connection 'c1' not found.");
	}
	/**
	 * getSecondCorner
	 * @param connection
	 * @return second corner of this connection
	 */
	public Corner getSecondCorner(Connection c1){
		for(int i=0; i<connections.length; i++){
			if(connections[i]==c1){
				return corners[(i+1)%6];
			}
		}
		throw new RuntimeException("Connection 'c1' not found.");
	}

	
	/**
	 * Set corners of this field
	 * @param corners - array with a length of 6
	 */
	public void setCorners(Corner[] corners){
		if(corners.length != x6E)
			throw new RuntimeException("Length must be 6.");
		this.corners = corners;
	}
	/**
	 * getCornerNumber
	 * @param c - corner
	 * @return number of corner 'c' (1...6, starting on top)
	 */
	public int getCornerNumber(Corner c){
		for(int i=0; i<corners.length; i++){
			if(corners[i]==c){
				return i+1;
			}
		}
		throw new RuntimeException("Corner 'c' not found.");
	}
	/**
	 * getCornerCoordinate - for initialization
	 * @param cornerNumber	- Starts at top (1...6)
	 * @return Coordinates (point) for this corner
	 */
	public Coordinate getCornerPosition(int cornerNumber){
		int index = (3+xAdd + cornerNumber) % x6E;
		Coordinate relative = outerCornersRelCoord[index];
		Coordinate total    = position.copy().add(relative);
		return total;
	}
	/**
	 * getCornerConnections - for initialization
	 * @param cornerNumber
	 * @return ArrayList with 2 to 3 neighboring connections
	 */
	public ArrayList<Connection> getCornerConnections(int cornerNumber){
		
		Connection[] tmpList = new Connection[4];
		ArrayList<Connection> result = new ArrayList<Connection>(3);
		Field f1, f2;
		
		tmpList[0] = getConnection(cornerNumber-1);			// left connection
		tmpList[1] = getConnection(cornerNumber  );			// right connection
		
		f1 = tmpList[0].getTarget(this);					// top connection possibility 1:
		if(f1!=null)										// -
			tmpList[2] = f1.previousConnection(tmpList[0]);	// -
		
		if(tmpList[2]==null){								// top connection possibility 2: (only if not already found)
			f2 = tmpList[1].getTarget(this);				// -
			if(f2!=null)									// -
				tmpList[3] = f2.nextConnection(tmpList[1]);	// -
		}
		
		for(Connection c1 : tmpList)						// add existing connections
			if(c1!=null)									// -
				result.add(c1);								// -
		
		return result;
	}
	/**
	 * getCornerFields - for initialization
	 * @param cornerNumber - 1 to 6 (higher and lower supported)
	 * @return ArrayList<Field> with 1 to 3 neighboring fields
	 */
	public ArrayList<Field> getCornerFields(int cornerNumber){
		
		ArrayList<Field> result = new ArrayList<Field>(3);
		Field f1, f2;
		
		Connection c1 = getConnection(cornerNumber-1);	// left connection
		Connection c2 = getConnection(cornerNumber  );	// right connection
		f1 = c1.getTarget(this);						// connected field f1
		f2 = c2.getTarget(this);						// connected field f2
		
		result.add(this);		// add first

		if(f1!=null)			// add second (if existent)
			result.add(f1);		// -
		
		if(f2!=null)			// add third (if existent)
			result.add(f2);		// -
		
		return result;
	}

	/**
	 * getCorner
	 * @param cornerNumber	- Starts at top (1...6)
	 * @return Corner
	 */
	public Corner getCorner(int cornerNumber){
		return corners[cornerNumber-1];
	}
	

	//-----------------------------------------------------------------
	// --- Painting ---------------------------------------------------
	//
	@Override
	public void paint(Graphics g){
		
		if(mhasBandit)
			g.setColor(Color.LIGHT_GRAY);
		else
			g.setColor(Color.GRAY);
		
		drawHexagon_rel(g, position, innerCornersRelCoord, true);
//		g.setColor(Color.GRAY);
//		drawHexagon_rel(g, position, innerCornersRelCoord, false);
//		drawHexagon_rel(g, position, outerCornersRelCoord, false);
		paintPicture(g);
		
//		Font font = g.getFont();
//		FontMetrics fontM = g.getFontMetrics();
//		
//		if(this.fieldID==1){
//			Out.pl();
//			Out.pl( "Font name  : " + font.getName() );
//			Out.pl( "Font size  : " + font.getSize() );
//			Out.pl( "Font size  : " + font.getSize2D() );
//			Out.pl( "Height     : " + fontM.getHeight() );
//			Out.pl( "'Text' size: " + fontM.stringWidth("Text") );
//			Out.pl( "Width      : " + fontM.charWidth('T') );
//		}
		
		g.setColor(Color.BLACK);
		String res = Resources.ResourcesEnum.values()[resourceNumber].name();
		drawStringCentered( res, positionT1, g );
		drawStringCentered( ""+diceNumber, positionT2, g );
		
		
		//g.drawString(str, x, y);
		
		//g.setColor(Color.RED);
		//g.drawRect(klickCoord.x1, klickCoord.y1, klickCoord.getWidth(1), klickCoord.getHeight(1));
	}
	
	/**
	 * drawStringCentered
	 * @param s
	 * @param pos
	 * @param g
	 */
	public static void drawStringCentered(String s, Coordinate pos, Graphics g){
		FontMetrics fontM = g.getFontMetrics();
		g.drawString(s, pos.x1-fontM.stringWidth(s)/2, pos.y1+fontM.getFont().getSize()/2);
	}

	//@Override
	public void paintPicture(Graphics g) {
		
		BufferedImage img = Pictures.fieldPictures[0];
		
		int r = size/2;
		int x1 = position.x1-r, y1 = position.y1-r,
			x2 = position.x1+r, y2 = position.y1+r;
		
		//g.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
////	g.drawImage(img, x1, y1, x2, y2, -1, -1, img.getWidth()+1, img.getHeight()+1, null);
		g.drawImage(img, 0,0,null);
		
//		
//		g.setColor(Color.BLACK);
//		g.drawString(""+getFieldNumber(), position.x1-7, position.y1+4);
	}
	
	public void paintClassical(Graphics g){
		g.setColor(Color.GRAY);
		drawHexagon_rel(g, position, innerCornersRelCoord, true);
		
		g.setColor(Color.BLACK);
		g.drawString(""+getFieldNumber(), position.x1-7, position.y1+4);
		
		//g.setColor(Color.RED);
		//g.drawRect(klickCoord.x1, klickCoord.y1, klickCoord.getWidth(1), klickCoord.getHeight(1));
	}
	
	/**
	 * Paint variation. 
	 * Just paints filled hexagon. Color will not be overwritten.
	 * @param g
	 */
	public void paintHexagon(Graphics g) {
		drawHexagon_rel(g, position, innerCornersRelCoord, true);
		//drawHexagon_rel(g, position, outerCornersRelCoord, true);
	}
	
	public int[][] getHexagon(){
		
		p.reset();
		
		for(Coordinate c : innerCornersRelCoord){
			Coordinate tmp = position.copy().add(c);
			p.addPoint(	tmp.x1, tmp.y1 );
		}
		
		int[][] ret = new int[2][];
		ret[0] = p.xpoints;
		ret[1] = p.ypoints;
		
		return ret;
	}
	
	/**
	 * calculateHexagon_rel
	 * Calculates relative coordinates for the hexagon.
	 * Coordinates match even if mirrored!
	 * 
	 * @param radius
	 */
	private static void calculateHexagon_rel(int radius) {
		
		int n = 6;									// 6 edges
		double turn = 0.5;							// half turn (0.5 * edgeToEdgeAngle)
		double turnRad = turn * 2*Math.PI / n;		// turn in radiant...
		
		double x = Math.cos(turnRad);
		double y = Math.sin(turnRad);
		double h = 1;
		
		int i=0;
		outerCornersRelCoord = new Coordinate[n];
		innerCornersRelCoord = new Coordinate[n];
		double radius2 = (radius*fieldSpaceFactor);
		
		outerCornersRelCoord[i] = new Coordinate(x*radius , y*radius );
		innerCornersRelCoord[i] = new Coordinate(x*radius2, y*radius2);
		//Out.pl("> Outer Corner ["+i+"] = "+outerCorners[i]);
		i++;
		outerCornersRelCoord[i] = new Coordinate(0*radius , h*radius );
		innerCornersRelCoord[i] = new Coordinate(0*radius2, h*radius2);
		//Out.pl("> Outer Corner ["+i+"] = "+outerCorners[i]);
		i++;		
		outerCornersRelCoord[i] = new Coordinate(-x*radius , y*radius );
		innerCornersRelCoord[i] = new Coordinate(-x*radius2, y*radius2);
		//Out.pl("> Outer Corner ["+i+"] = "+outerCorners[i]);
		i++;
		outerCornersRelCoord[i] = new Coordinate(-x*radius , -y*radius );
		innerCornersRelCoord[i] = new Coordinate(-x*radius2, -y*radius2);
		//Out.pl("> Outer Corner ["+i+"] = "+outerCorners[i]);
		i++;
		outerCornersRelCoord[i] = new Coordinate(0*radius , -h*radius );
		innerCornersRelCoord[i] = new Coordinate(0*radius2, -h*radius2);
		//Out.pl("> Outer Corner ["+i+"] = "+outerCorners[i]);
		i++;	
		outerCornersRelCoord[i] = new Coordinate(x*radius , -y*radius );
		innerCornersRelCoord[i] = new Coordinate(x*radius2, -y*radius2);
		//Out.pl("> Outer Corner ["+i+"] = "+outerCorners[i]);
		//i++;
	}
	
	/**
	 * Draws the hexagon with the relative coordinates (calculated before)
	 * @param g
	 * @param corners
	 * @param filled
	 */
	private static void drawHexagon_rel(Graphics g, Coordinate position, Coordinate[] corners, boolean filled) {
		
		p.reset();
		
		for(Coordinate c : corners){
			Coordinate tmp = position.copy().add(c);
			p.addPoint(	tmp.x1, tmp.y1 );
		}
		
		if (filled)
			g.fillPolygon(p);
		else
			g.drawPolygon(p);
	}
	
	/*
	private static void calculate6Eck_relMath(int radius) {
		
		int n = 6;									// 6 edges
		double turn = 0.5;							// half turn (0.5 * edgeToEdgeAngle)
		double turnRad = turn * 2*Math.PI / n;	// turn in radiant...
		
		outerCornersRelCoord = new Coordinate[n];
		innerCornersRelCoord = new Coordinate[n];
		int radius2 = (int) (radius*fieldSpaceFactor);
		
		for (int i = 0; i < n; i++){
			outerCornersRelCoord[i] = new Coordinate( 	
					(int)(radius * Math.cos(i * 2*Math.PI / n + turnRad)),
					(int)(radius * Math.sin(i * 2*Math.PI / n + turnRad))  );
			
			innerCornersRelCoord[i] = new Coordinate( 	
					(int)(radius2 * Math.cos(i * 2*Math.PI / n + turnRad)),
					(int)(radius2 * Math.sin(i * 2*Math.PI / n + turnRad))  );
			
			Out.pl("> Outer Corner ["+i+"] = "+outerCornersRelCoord[i]);
		}
	}
	
	//**
	// * Draw this shape...
	private static void draw6EckMath(Graphics g, int x, int y, int radius, boolean filled) {

		//if(p.npoints<9999){
		//p.
			//p.contains(x, y);
			p.reset();
			int n = 6;
			double turn = 0.5;
			double turnRad = turn * 2 * Math.PI / n;
		
			for (int i = 0; i < n; i++)
				p.addPoint(	(int) (x + radius * Math.cos(i * 2 * Math.PI / n + turnRad)),
							(int) (y + radius * Math.sin(i * 2 * Math.PI / n + turnRad)) );
		//}
		
		if (filled)
			g.fillPolygon(p);
		else
			g.drawPolygon(p);
	}
	*/
	
	/**
	 * Draw an "+"
	 */
	public static void drawX(Graphics g, Coordinate c, int radius) {
		//g.setColor(Color.GRAY);
		g.drawLine(c.x1-radius, c.y1       , c.x1+radius, c.y1       );
		g.drawLine(c.x1       , c.y1-radius, c.x1       , c.y1+radius);
	}
	
	
	//-----------------------------------------------------------------
	// --- Klickable methods ------------------------------------------
	//
	
	@Override
	public Coordinate getPosition(){
		return position;
	}
	@Override
	public Coordinate getKlickArea(){
		return klickCoord;
	}
	@Override
	public boolean isKlickedBy(int x, int y) {
		return klickCoord.contains(x, y);
	}
	@Override
	public boolean klickIt() {
		Out.pl("Klicked: " + this + " ("+this.position+")");
		if(!mhasBandit){
			mhasBandit = true;
			return true;
		}
		return false;
	}

	
	//-----------------------------------------------------------------
	// --- Other methods ----------------------------------------------
	//
	
	@Override
	public String toString(){
		return "'"+fieldID+"'";
	}
}
